﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using Kiiro.Common.Entities;
using Kiiro.Common.Utility;
using Kiiro.Logging;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;

namespace Kiiro.Migration.From_1_0_4_2_To_1_1_0_1
{
    public class Migrator : SPFeatureReceiver
    {
        public override void FeatureInstalled ( SPFeatureReceiverProperties properties ) { }

        public override void FeatureUninstalling ( SPFeatureReceiverProperties properties ) { }

        public override void FeatureDeactivating ( SPFeatureReceiverProperties properties ) { }

        public override void FeatureActivated ( SPFeatureReceiverProperties properties )
        {
            LoggingManager.LogMessage ( "TRACE", "Kiiro.Migration from 1.0.4.2 to 1.1.0.1 has begun" );

            SPWebService webService = properties.Definition.Farm.Services.GetValue<SPWebService> ( );

            foreach ( SPWebApplication webApplication in webService.WebApplications )
            {
                foreach ( SPSite site in webApplication.Sites )
                {
                    using ( SPSite systemSite = SPHelpers.GetSystemSite ( site.ID ) )
                    {
                        foreach ( SPWeb web in systemSite.AllWebs )
                        {
                            if ( SPHelpers.IsPortal ( web ) )
                            {
                                FixPortalGroups ( web );
                                FixIsAdminFlag ( web );
                            }
                            else if ( SPHelpers.IsProject ( web ) )
                                FixProjectGroups ( web );

                            web.Dispose ( );
                        }
                    }

                    site.Dispose ( );
                }
            }

            LoggingManager.LogMessage ( "TRACE", "Kiiro.Migration from 1.0.4.2 to 1.1.0.1 has ended\r\n\r\n" );
        }

        private void FixPortalGroups ( SPWeb web )
        {
            List<string> groupNames = new List<string> ( );

            #region Owners
            groupNames = new List<string> { Constants.AppName.Proper + " SharePoint Administrators", Constants.AppName.Proper + " Administrators" };

            web.SiteGroups.Cast<SPGroup> ( ).Where ( x => groupNames.Contains ( x.Name ) ).ToList ( )
                .SelectMany<SPGroup, SPUser> ( y => y.Users.Cast<SPUser> ( ) ).ToList ( )
                    .ForEach ( delegate ( SPUser user ) { TryAddUser ( user, web, web.AssociatedOwnerGroup ); } );

            groupNames.ForEach ( delegate ( string name ) { TryRemoveGroup ( name, web ); } );
            #endregion

            #region Members
            groupNames = new List<string> { Constants.AppName.Proper + " System Accounts", Constants.AppName.Proper + " Users" };

            web.SiteGroups.Cast<SPGroup> ( ).Where ( x => groupNames.Contains ( x.Name ) ).ToList ( )
                .SelectMany<SPGroup, SPUser> ( y => y.Users.Cast<SPUser> ( ) ).ToList ( )
                    .ForEach ( delegate ( SPUser user ) { TryAddUser ( user, web, web.AssociatedMemberGroup ); } );

            groupNames.ForEach ( delegate ( string name ) { TryRemoveGroup ( name, web ); } );
            #endregion

            #region Visitors
            groupNames = new List<string> { Constants.AppName.Proper + " Visitors" };

            web.SiteGroups.Cast<SPGroup> ( ).Where ( x => groupNames.Contains ( x.Name ) ).ToList ( )
                .SelectMany<SPGroup, SPUser> ( y => y.Users.Cast<SPUser> ( ) ).ToList ( )
                    .ForEach ( delegate ( SPUser user ) { TryAddUser ( user, web, web.AssociatedVisitorGroup ); } );

            groupNames.ForEach ( delegate ( string name ) { TryRemoveGroup ( name, web ); } );
            #endregion
        }

        private void FixIsAdminFlag ( SPWeb web )
        {
            try
            {
                web.AllowUnsafeUpdates = true;

                string queryText = "<Where><Eq><FieldRef Name='{0}'/><Value Type='Boolean'>1</Value></Eq></Where>";
                SPQuery query = new SPQuery { Query = string.Format ( queryText, Constants.UserFieldNames.IsAdministrator ) };

                SPList list = web.Lists [ Constants.SPListNames.Users ];
                SPListItemCollection items = list.GetItems ( query );

                if ( items != null && items.Count > 0 )
                {
                    foreach ( SPListItem item in items )
                    {
                        item [ Constants.UserFieldNames.ManageProjects ] = false;

                        item.Update ( );
                    }
                }
            }
            catch ( Exception ex )
            {
                LoggingManager.LogMessage ( "ERROR", string.Format ( "Error updating user flags \r\n{0}", ex.StackTrace ) );
            }
            finally
            {
                web.AllowUnsafeUpdates = false;
            }
        }

        private void FixProjectGroups ( SPWeb web )
        {
            List<string> groupNames = new List<string> ( );

            #region Owners
            groupNames = new List<string> { web.Title + " Project Managers" };

            web.SiteGroups.Cast<SPGroup> ( ).Where ( x => groupNames.Contains ( x.Name ) ).ToList ( )
                .SelectMany<SPGroup, SPUser> ( y => y.Users.Cast<SPUser> ( ) ).ToList ( )
                    .ForEach ( delegate ( SPUser user ) { TryAddUser ( user, web, web.AssociatedOwnerGroup ); } );

            groupNames.ForEach ( delegate ( string name ) { TryRemoveGroup ( name, web ); } );
            #endregion

            #region Members
            groupNames = new List<string> { web.Title + " Team Members" };

            web.SiteGroups.Cast<SPGroup> ( ).Where ( x => groupNames.Contains ( x.Name ) ).ToList ( )
                .SelectMany<SPGroup, SPUser> ( y => y.Users.Cast<SPUser> ( ) ).ToList ( )
                    .ForEach ( delegate ( SPUser user ) { TryAddUser ( user, web, web.AssociatedMemberGroup ); } );

            groupNames.ForEach ( delegate ( string name ) { TryRemoveGroup ( name, web ); } );
            #endregion

            #region Visitors
            groupNames = new List<string> { web.Title + " Visitors" };

            web.SiteGroups.Cast<SPGroup> ( ).Where ( x => groupNames.Contains ( x.Name ) ).ToList ( )
                .SelectMany<SPGroup, SPUser> ( y => y.Users.Cast<SPUser> ( ) ).ToList ( )
                    .ForEach ( delegate ( SPUser user )
            {
                if ( user.LoginName != Constants.Security.AD_AUTH_USERS_DOMAIN_GROUP_NAME )
                    TryAddUser ( user, web, web.AssociatedVisitorGroup ); ;
            } );

            groupNames.ForEach ( delegate ( string name ) { TryRemoveGroup ( name, web ); } );
            #endregion
        }

        private void TryAddUser ( SPUser user, SPWeb web, SPGroup group )
        {
            try
            {
                if ( !user.LoginName.ToLower ( ).StartsWith ( "builtin" ) )
                {
                    web.EnsureUser ( user.LoginName );
                    group.AddUser ( user );
                }
            }
            catch ( Exception ex )
            {
                LoggingManager.LogMessage ( "ERROR", string.Format ( "Error adding user: {0} to web: {1} group: {2} \r\n{3}",
                    user.LoginName, web.Title, group.Name, ex.StackTrace ) );
            }
        }

        private void TryRemoveGroup ( string name, SPWeb web )
        {
            try
            {
                web.SiteGroups.Remove ( name );
                web.Update ( );
            }
            catch ( Exception ex )
            {
                LoggingManager.LogMessage ( "ERROR", string.Format ( "Error removing group {0} \r\n{1}", name, ex.StackTrace ) );
            }
        }
    }
}
